/**
 *  \file   main.c
 *  \brief  eZ430-RF2500 tutorial
 *  \author Miora ANDRIAMANOHARISOA, Loic MINGHETTI
 *  \date   PFE 2011
 **/

#include "include.h"

#define NUM_PT 4
static struct pt pt[NUM_PT];

/*************************************************************
 			THREAD CAPTURE
*************************************************************/

/* protothread gérant la capture périodique de la température */
static PT_THREAD(thread_temperature_capture(struct pt *pt))
{
    PT_BEGIN(pt);

    while(1)
    {
      TIMER_TEMPERATURE_CAPTURE = 0;
      PT_WAIT_UNTIL(pt, timer_reached(TIMER_TEMPERATURE_CAPTURE, temperature_capture_timeout));
      add_temperature(adc10_sample_temp());
    }

    PT_END(pt);
}

/*************************************************************
 			THREAD INTERFACE UTILISATEUR
*************************************************************/

/* bouton */
static int antibouncing_flag;
static int button_pressed_flag;

void button_antibouncing_elapsed() {
	antibouncing_flag = 0;
}

/* fonction de call back appelé à chaque interruption (pression bouton) : déclare le noeud comme configuré*/
void button_pressed_cb()
{
    if(antibouncing_flag == 0)
    {
        button_pressed_flag = 1;
        antibouncing_flag = 1;
        set_configuration_defined();
    }
}

/* UART */
#define UART_MAX_CHARS 10
#define UART_READ_IDLE 0
#define UART_READ_INT 1
#define UART_EXIT_PRESSED 2
#define UART_ENTER_PRESSED 3
static int uart_state = UART_READ_IDLE;
static int uart_read_pos = -1;
static char uart_data[UART_MAX_CHARS];

int uart_cb(uint8_t data) {
  if (uart_state == UART_READ_INT) {
    switch (data) {
      case 0x1B : uart_state = UART_EXIT_PRESSED; break;
      case 0x8 : 
	if (uart_read_pos > -1) {
	  printf("%c %c", 0x8, 0x8);
	  uart_read_pos--;
	}
	break;
      case 0xA : 
      case 0xB : 
      case 0xC : 
      case 0xD : uart_state = UART_ENTER_PRESSED; break;
      default :
	if (uart_read_pos+2 < UART_MAX_CHARS && data >= 0x30 && data <= 0x39) {
	  printf("%c", (unsigned char) data);
	  uart_data[++uart_read_pos] = (unsigned char) data;
	}
	break;
    }
  }
  return 0;
}

static unsigned int uart_get_int() {
  uart_data[++uart_read_pos] = '\0';
  return atoi(uart_data);
}

static void uart_reset () {
	uart_read_pos = -1;
	uart_state = UART_READ_IDLE;
}

static void print_header() {
  int temp;
  temp = adc10_sample_temp() + OFFSET;
  printf("\r\n\
  +-------------------------------------------------------------------+\r\n\
  |      Temperature Monitoring Wireless Sensor Network      	      |\r\n\
  |                  - Node initialisation -                          |\r\n\
  +-------------------------------------------------------------------+\r\n\
  \n\n\
  Node ID : %u\r\n\
  Node hop count to sink : %u\r\n\
  Current temperature : %d.%d %cC\r\n\
  \n", NODE_ID, HOP_COUNT, temp/10, temp%10, 0xA7);
}

/* interface */
static unsigned int menu;

PT_THREAD(thread_ui(struct pt *pt)){
  unsigned int new_id;
  unsigned int new_hop_count;
  int new_temperature;
  PT_BEGIN(pt);

  while(node_configuration() == UNDEFINED) {
    uart_reset();
    uart_state = UART_READ_INT;
    button_pressed_flag = 0;

    if (NODE_ID > 0) {
      print_header();
      printf ("1. Change node ID\r\n2. Change node hop count\r\n3. Calibrate sensor\r\n4. Exit\r\n");
      printf ("\n\nSelect your choice and hit enter : ");
      
      PT_WAIT_UNTIL(pt, uart_state == UART_ENTER_PRESSED || uart_state == UART_EXIT_PRESSED);
      if (uart_state == UART_EXIT_PRESSED) PT_RESTART(pt);
      menu = uart_get_int();
      } 
    
    if (menu == 1) {
      print_header();
      printf ("\n\nNew ID must be unique and between 10 and 99. \r\nTo cancel, press ESC.\r\n\nType new ID and hit enter : ");
      uart_reset();
      uart_state = UART_READ_INT;
      
      PT_WAIT_UNTIL(pt, uart_state == UART_ENTER_PRESSED || uart_state == UART_EXIT_PRESSED || button_pressed_flag);
      if (button_pressed_flag || uart_state == UART_EXIT_PRESSED) PT_RESTART(pt);
      new_id = uart_get_int();
      if (new_id > 99 || new_id < 10) {
	printf ("\r\n\nInvalid ID. Press enter to continue.");
      } else {
	NODE_ID = new_id;
	printf ("\r\n\nNew ID is %u. Press enter to continue.", NODE_ID);
      }
      uart_reset();
      uart_state = UART_READ_INT;
      flash_save_node_id();
      set_node_id_defined();
      
      PT_WAIT_UNTIL(pt, uart_state == UART_ENTER_PRESSED || uart_state == UART_EXIT_PRESSED || button_pressed_flag);
      PT_RESTART(pt);
	    
    } 
    
    else if (menu == 2) {
      print_header();
      printf ("\n\nHop count must be between 1 and 9. \r\nTo cancel, press ESC.\r\n\nType hop count and hit enter : ");
      uart_reset();
      uart_state = UART_READ_INT;
      
      PT_WAIT_UNTIL(pt, uart_state == UART_ENTER_PRESSED || uart_state == UART_EXIT_PRESSED || button_pressed_flag);
      if (button_pressed_flag || uart_state == UART_EXIT_PRESSED) PT_RESTART(pt);
      new_hop_count = uart_get_int();
      if (new_hop_count > 9 || new_hop_count < 0) {
	printf ("\r\n\nInvalid hop_count. Press enter to continue.");
      } else {
	HOP_COUNT = new_hop_count;
	printf ("\r\n\nNew hop count is %u. Press enter to continue.", HOP_COUNT);
      }
      uart_reset();
      uart_state = UART_READ_INT;
      flash_save_hop_count();
      set_hop_count_defined();
      
      PT_WAIT_UNTIL(pt, uart_state == UART_ENTER_PRESSED || uart_state == UART_EXIT_PRESSED || button_pressed_flag);
      PT_RESTART(pt); 
    }
    
    else if (menu == 3) {
      print_header();
      printf ("\n\nTo calibrate sensor, set current room temperature in tenths of degree celsius (eg. 225 for 22.5 deg).\r\nTo cancel, press ESC.\r\n\nType temperature and hit enter : ");
      uart_reset();
      uart_state = UART_READ_INT;
      
      PT_WAIT_UNTIL(pt, uart_state == UART_ENTER_PRESSED || uart_state == UART_EXIT_PRESSED || button_pressed_flag);
      if (button_pressed_flag || uart_state == UART_EXIT_PRESSED) PT_RESTART(pt);
      new_temperature = uart_get_int();
      printf ("\r\n\nNew temperature is %d. Press enter to continue.", new_temperature);
      set_real_temperature(new_temperature);
      
      uart_reset();
      uart_state = UART_READ_INT;
      flash_save_sensor_offset();
      set_offset_defined();

      PT_WAIT_UNTIL(pt, uart_state == UART_ENTER_PRESSED || uart_state == UART_EXIT_PRESSED || button_pressed_flag);
      PT_RESTART(pt);

    }
    
    else {
	    PT_RESTART(pt);
    }
    
    uart_reset();
  }

  PT_END(pt);
}


/*************************************************************
 			THREAD ENVOI
*************************************************************/

#define NB_PREAMBULE 50 
#define NB_REVEIL_MAX 3
#define NB_SEND_TRYING_MAX 3
static int jeton; 
static int flag_action; 
static int nb_send_trying;
static int MODE_ENVOI;
static int nb_reveil;
static int NUM_MESURE; 
static int CORRESPONDENT;
static int MSG_TYPE;
static int num_preambule;
static int send_phase2_is_valid;
static int rcv_phase1_is_valid;
static int rcv_phase3_is_valid;
static int scan_is_valid;
static int slot;

void context_init(void) {
 CORRESPONDENT = BROADCAST;
 MSG_TYPE = 0;
 num_preambule = 0;
 flag_action = 1;
 send_phase2_is_valid = 0;
 rcv_phase1_is_valid = 0;
 rcv_phase3_is_valid = 0;
 scan_is_valid = 0;
 slot = 0;
 tab_concurrent_init();
}

static PT_THREAD(thread_send_temperature(struct pt *pt)){
  PT_BEGIN(pt);
  while(1){
    context_init();
    unsigned long val;
    char trame[12];
    PT_YIELD_UNTIL(pt,(jeton==1));
    TIMER_AWAKE_INTERVAL = 0;
    PT_YIELD_UNTIL(pt,timer_reached(TIMER_AWAKE_INTERVAL,snd_rcv_timeout));
    
    /* MODE_ENVOI = 1 si l'on est en relai, sinon 0 pour envoyer la température que l'on a mesuré */
    if ((check_temperature_to_relay_array() == 0) && (nb_reveil == NB_REVEIL_MAX))  {
      MODE_ENVOI = 0;
    }
    else {
      MODE_ENVOI = 1;
    }
    if (nb_send_trying != NB_SEND_TRYING_MAX) {
      printf("---------------------- SEND BEGIN ---------------------\r\n");
      while (flag_action != 0 ) { 
	re_init_buffer();
	switch (flag_action){
	  
	  case 1 :
	    
	    TIMER_ACTION = 0;
	    printf("[SEND]SCAN\r\n");
	    TIMER_ACTION = 0;
	    rx_start();
	    while ((timer_not_reached(TIMER_ACTION, scan_timeout))&&(scan_is_valid == 0)){
	      TIMER_TEST = 0;
	      rx_start();
	      Msg msg = rx();	    
	      while (timer_not_reached(TIMER_TEST, 8)) {}; 
	      if(msg.is_valid == 1) {
		scan_is_valid = 1;
	      }
	    }
	    radio_sleep();
	    
	    if (scan_is_valid == 1) {
	      flag_action = 0;
	      jeton = 0;
	      nb_reveil = 0;
	    } else flag_action = 2;
	    break;
	  
	  case 2 :
	    
	      num_preambule = 0;
	      MSG_TYPE = 1;
	      printf("[SEND]PREAMBULE period\r\n");
	      for(num_preambule = 0; num_preambule < NB_PREAMBULE; num_preambule++) {
		TIMER_ACTION = 0;
		
		val = (unsigned long)NODE_ID * (unsigned long)1000000 + (unsigned long)HOP_COUNT* (unsigned long)100000+ (unsigned long)BROADCAST * (unsigned long)1000+ (unsigned long)MSG_TYPE * (unsigned long)100 + (unsigned long)num_preambule;   
		ltoa(val,trame,10);
		tx(trame);   
		while (timer_not_reached(TIMER_ACTION, preambule_timeout)) { }
	      }
	      flag_action = 3;
	    break;
	    
	  case 3 :
	    
	    printf("[SEND]ACK DATA waiting\r\n "); 
	    TIMER_ACTION = 0;
	    rx_start();
	    while (timer_not_reached(TIMER_ACTION, (ack_data_timeout + CLOCK_GAP_PA1_MAX + CLOCK_GAP_PA2_MAX + SECURITY_MARGIN ))){
	      TIMER_TEST = 0;
	      rx_start();
	      Msg msg = rx();	    
	      while (timer_not_reached(TIMER_TEST, 8)) {};
	      if(msg.is_valid == 1) {
		if (msg.type == 2) {
		  if (msg.destinataire == NODE_ID) {  
		    if (MODE_ENVOI == 0) {
		      add_concurrent(msg.source, msg.hop_count, msg.rssi);
		      send_phase2_is_valid = 1;  
		    } else if (MODE_ENVOI == 1) {
		      if ((msg.source != temperature_to_relay_array[0].origine_id) && (msg.source != temperature_to_relay_array[0].sender_id) ) {
			add_concurrent(msg.source, msg.hop_count, msg.rssi);
			send_phase2_is_valid = 1;
		      } else {
			printf("[SEND]Impossible relay : the message had already reached this candidate\r\n");
		      }
		    } 
		  } 
		} 
	      } 
	    } 
	    radio_sleep();

	    if (send_phase2_is_valid == 1) { 
	      affiche_tab_concurrent();
	      CORRESPONDENT = choose_relay();
	      printf("[SEND]Relay choosen : %d \r\n", CORRESPONDENT);    
	      determine_hop_count();
	      flag_action = 4;
	    } else {
	      
	      nb_send_trying = nb_send_trying + 1;
	      
	      flag_action = 0;
	      printf("[SEND]No candidate\r\n");
	    }  
	    break;
	  
	  case 4 : 
	    
	    MSG_TYPE = 3;
	    int temperature;
	    int id_origine;
	    int num_mesure;
	    char temp_c[4];  
	    char id_origine_c[3];
	    char num_mesure_c[3];

	    if (MODE_ENVOI == 0) {  
	      temperature = temperature_average() + OFFSET;
	      id_origine = NODE_ID;
	      if (NUM_MESURE < 99) {
		NUM_MESURE = NUM_MESURE +1;
	      } else NUM_MESURE = 0;
	      num_mesure = NUM_MESURE;
	    } else if (MODE_ENVOI == 1){
	      tmp_to_relay value;
	      value = get_temperature_to_relay();
	      id_origine = value.origine_id;    
	      temperature = value.temperature;
	      num_mesure = value.num_mesure;
	    } else { 
	      printf("MODE ENVOI INCONNU\r\n");
	    }
	    
	    val = NODE_ID * (unsigned long)1000000 + HOP_COUNT * (unsigned long)100000 + BROADCAST * (unsigned long)1000 + MSG_TYPE * (unsigned long)100 + CORRESPONDENT;
	    ltoa(val,trame,10); 
	    itoa(temperature, temp_c,10);
	    itoa(id_origine, id_origine_c,10);
	    itoa(num_mesure, num_mesure_c, 10);
	    
	    sprintf(trame,"%s%s%s%s", trame, temp_c, id_origine_c, num_mesure_c);         
    	    TIMER_ACTION = 0;
	    while (timer_not_reached(TIMER_ACTION, 8)) {}       
	    tx(trame);
	    printf("[SEND] DATA %s sent\r\n", trame); 
	    
	    flag_action = 0;
	    jeton = 0;
	    nb_reveil = 0;
	    break;
	}
      }
      printf("---------------------- SEND END ---------------------\r\n\n");
    } else {
      jeton = 0;
      nb_send_trying = 0;
      nb_reveil = 0;
    }
  }
  PT_END(pt);  
}

/*************************************************************
 			THREAD RECEPTION
*************************************************************/

static PT_THREAD(thread_read_radio(struct pt *pt)) {

  PT_BEGIN(pt);
  
  while(1){
    char trame[7];
    unsigned long val;
    Msg msg;
    context_init();
    PT_YIELD_UNTIL(pt,(jeton==0));
    TIMER_AWAKE_INTERVAL = 0;   
    PT_YIELD_UNTIL(pt,timer_reached(TIMER_AWAKE_INTERVAL, snd_rcv_timeout));
    nb_reveil = nb_reveil + 1;
    
    if (HOP_COUNT != 0) {
      printf("---------------------- RECEIVE BEGIN -------------------\r\n");
    }

    while (flag_action!=0) {

      
      re_init_buffer();
      switch (flag_action) {
	case 1 :
	  
	  TIMER_ACTION = 0;
	  rx_start();
 	  while (timer_not_reached(TIMER_ACTION, awoken_timeout) && (rcv_phase1_is_valid == 0)){
	    TIMER_TEST = 0;
	    rx_start();
	    msg = rx();	    
    	    while (timer_not_reached(TIMER_TEST, 8)) {};
	    if (msg.is_valid == 1) {
	      if (msg.type == 1) {
		rcv_phase1_is_valid = 1;
	      }
 	    }
	  }
	  radio_sleep();
	   
	  if (rcv_phase1_is_valid == 1) {
	    CORRESPONDENT = msg.source;
	    int sender_hop_count = msg.hop_count;
	    int num_preambule = msg.num_preambule; 
	    int num_slot;
	    num_slot = -1;
	    while (num_slot < 0) {
	      num_slot = rand()%3;
	    }
	    
	    backoff_timeout = (NB_PREAMBULE - num_preambule - 1) * preambule_timeout + (HOP_COUNT - sender_hop_count + 1) * (ack_data_timeout/3) + num_slot * SLOT_PERIOD;	    
	    
	    TIMER_ACTION = 0;
	    while (timer_not_reached(TIMER_ACTION, (backoff_timeout + CLOCK_GAP_PA1_MAX + SECURITY_MARGIN))){ }   
	    flag_action = 2;

	  } else flag_action = 0;

	break;

	case 2 :
	  
	  MSG_TYPE = 2;
	  val = NODE_ID * (unsigned long)10000 + HOP_COUNT * (unsigned long)1000 + CORRESPONDENT * (unsigned long)10 + MSG_TYPE; 
	  ltoa(val,trame,10);
	  tx(trame);   
	  printf("[RCV]ACK_DATA :  %s envoyé\r\n", trame);  
	  flag_action = 3;
	  break;
	  
	case 3 :
	  printf("[RCV]WAIT DATA\r\n");
	  
   	  TIMER_ACTION = 0;
	  rx_start();
 	  while (rcv_phase3_is_valid != 1 && (timer_not_reached(TIMER_ACTION, (ack_data_timeout + CLOCK_GAP_PA1_MAX + (2*(CLOCK_GAP_PA2_MAX + SECURITY_MARGIN)))))){
	    TIMER_TEST = 0;
	    rx_start();
	    msg = rx();	    
    	    while (timer_not_reached(TIMER_TEST, 8)) {};
    	    if (msg.is_valid == 1) {
	      if (msg.type == 3) {
		rcv_phase3_is_valid = 1;

	      } 
 	    }  
	  }
	  radio_sleep();
  
	  if (rcv_phase3_is_valid == 1) {
	    CORRESPONDENT = msg.source;
	    if (msg.id_relay == NODE_ID) {
	      if (HOP_COUNT !=0) {
		printf("[RCV]This node will be relay in the next awoken period\r\n");
	      } else printf("\r\n**%d:%d:%d", msg.origine_id, msg.num_mesure, msg.temperature);
	      if (HOP_COUNT != 0) {
		add_temperature_to_relay(msg.origine_id, msg.source, msg.temperature, msg.num_mesure);
		affiche_temperature_to_relay_array();
	      }
	    }
	  } else {
	    printf ("[RCV]Blank ...\r\n"); 
	  }
	  flag_action = 0;
	  break;
      }
      
     }
     radio_sleep();
    
    /* le puits reste toujours en écoute */
     if (HOP_COUNT == 0){
       jeton = 0;
       if ( (nb_reveil > NB_REVEIL_MAX)) {
	NUM_MESURE++;
	printf("\r\n**%d:%d:%d",NODE_ID, NUM_MESURE, temperature_average());
	nb_reveil = 0;
      }
     }
 
     else {
      if ( (nb_reveil < NB_REVEIL_MAX) && ( check_temperature_to_relay_array() == 0) ) {
	jeton = 0;
      } else jeton = 1;
      printf("---------------------- RECEIVE END ---------------------\r\n\n");

    }
     
  }
  PT_END(pt);
  
}

/*************************************************************
 			PROGRAMME PRINCIPAL
*************************************************************/

int main(void)
{
    watchdog_stop();

    /* protothreads init */
    int i;
    for(i = 0; i < NUM_PT; i++)
    {
        PT_INIT(&pt[i]);
    }
    
    /* clock init */
    set_mcu_speed_dco_mclk_16MHz_smclk_8MHz();

    /* LEDs init */
    leds_init();

    /* timer init */
    timerA_init();
    timerA_register_cb(&timer_tick_cb);
    timerA_start_milliseconds(TIMER_PERIOD_MS);
    timerA_set_wakeup(1);

    /* button init */
    button_init();
    button_register_cb(button_pressed_cb);
    antibouncing_flag = 0;
    button_pressed_flag = 0;
    button_enable_interrupt();
 
    /* UART init (serial link) */
    uart_init(UART_9600_SMCLK_8MHZ);
    uart_register_cb(uart_cb);
    uart_data[0] = '\n';
    uart_state = UART_READ_IDLE;
    uart_read_pos = -1;

    /* ADC10 init (temperature) */
    adc10_start();
    
    /* Radio init */
    radio_init();
        
    /* retrieve node id from flash */
    flash_allocation_init();
    init_configuration();

    /* Temperature array init */
    init_temperature_array(300);
    init_temperature_to_relay_array();
    nb_reveil = 0;
    nb_send_trying = 0;
    NUM_MESURE = 0;
   
    __enable_interrupt();

    while (node_configuration() == UNDEFINED) {
      thread_ui(&pt[0]);
    }
    
        
    printf("\
    +-------------------------------------------------------------------+\r\n\
    | 			Thank you for configuring the node  		|\r\n\
    +-------------------------------------------------------------------+\r\n");
    print_header();  
    printf("\r\n\
    +-------------------------------------------------------------------+\r\n\
    | 				1-HOP START  				|\r\n\
    +-------------------------------------------------------------------+\r\n"); 

    while(1) {
      thread_temperature_capture(&pt[1]);
      thread_send_temperature(&pt[2]);
      thread_read_radio(&pt[3]);
      LPM3;
    }
}


